 /* 
 * File:   cTank.cpp
 * Author: josep
 * 
 * Created on 28 / desembre / 2010, 22:53
 */

#include "cTank.h"
#include "cVisionEvent.h"
#include "cBullet.h"

cTank::cTank() {
    ObjetctType=TANK_TYPE;
    localizedEnemy=false;
    ShotInterval = 100;
    timeToFire=0;
    SetFrameRate(40);
}

cTank::cTank(const cTank& orig):cAutonomousAgent(orig) {
}

cTank::~cTank() {
}

void cTank::Init(cTexture* texture, int PosX, int PosY, int Width, int Height){
    cAutonomousAgent::Init(texture, PosX, PosY, Width, Height);
    cTank::Init();
}

cPhysicalObject* cTank::popChild(){
    //sm->processTransition(GO_AHEAD);
    localizedEnemy=false;
    return cAutonomousAgent::popChild();
}

void cTank::Init(){
    FromLogicToViewCorrespondence.resize(9);
    FromLogicToViewCorrespondence[0]=8;
    FromLogicToViewCorrespondence[1]=1;
    FromLogicToViewCorrespondence[2]=0;
    FromLogicToViewCorrespondence[3]=3;
    FromLogicToViewCorrespondence[4]=2;
    FromLogicToViewCorrespondence[5]=5;
    FromLogicToViewCorrespondence[6]=4;
    FromLogicToViewCorrespondence[7]=7;
    FromLogicToViewCorrespondence[8]=6;

    StateMachine<cTank> *vSm = new StateMachine<cTank>();

    for(int i=0; i<9; i++){
        vSm->addState(this);
    }
    vSm->getState(OFF)->setName("Off");
    vSm->getState(MOVING_LEFT)->setName("Moving to left");
    vSm->getState(MOVING_RIGHT)->setName("Moving to right");
    vSm->getState(SHOOTING_LEFT)->setName("Shooting to left");
    vSm->getState(SHOOTING_RIGHT)->setName("Shooting to right");
    vSm->getState(BURNING_LEFT)->setName("Burning");
    vSm->getState(BURNING_RIGHT)->setName("Burning");
    vSm->getState(DEATH)->setName("Death");

    vSm->getState(OFF)->setTransition(TO_LEFT, vSm->getState(MOVING_LEFT));
    vSm->getState(OFF)->setTransition(TO_RIGHT, vSm->getState(MOVING_RIGHT));
    vSm->getState(OFF)->setTransition(REACHED, vSm->getState(BURNING_LEFT));

    vSm->getState(MOVING_LEFT)->setTransition(REACHED, vSm->getState(BURNING_LEFT));
    vSm->getState(MOVING_LEFT)->setTransition(TO_RIGHT, vSm->getState(MOVING_RIGHT));
    vSm->getState(MOVING_LEFT)->setTransition(SHOOT, vSm->getState(SHOOTING_LEFT));

    vSm->getState(MOVING_RIGHT)->setTransition(REACHED, vSm->getState(BURNING_RIGHT));
    vSm->getState(MOVING_RIGHT)->setTransition(TO_LEFT, vSm->getState(MOVING_LEFT));
    vSm->getState(MOVING_RIGHT)->setTransition(SHOOT, vSm->getState(SHOOTING_RIGHT));

    vSm->getState(SHOOTING_LEFT)->setTransition(REACHED, vSm->getState(BURNING_LEFT));
    //vSm->getState(SHOOTING_LEFT)->setTransition(TO_LEFT, vSm->getState(MOVING_LEFT));
    //vSm->getState(SHOOTING_LEFT)->setTransition(TO_RIGHT, vSm->getState(MOVING_RIGHT));
    //vSm->getState(SHOOTING_LEFT)->setTransition(SHOOT, vSm->getState(SHOOTING_LEFT));
    vSm->getState(SHOOTING_LEFT)->setTransition(GO_AHEAD, vSm->getState(MOVING_LEFT));

    vSm->getState(SHOOTING_RIGHT)->setTransition(REACHED, vSm->getState(BURNING_RIGHT));
    //vSm->getState(SHOOTING_RIGHT)->setTransition(TO_LEFT, vSm->getState(MOVING_LEFT));
    //vSm->getState(SHOOTING_RIGHT)->setTransition(TO_RIGHT, vSm->getState(MOVING_RIGHT));
    //vSm->getState(SHOOTING_RIGHT)->setTransition(SHOOT, vSm->getState(SHOOTING_RIGHT));
    vSm->getState(SHOOTING_RIGHT)->setTransition(GO_AHEAD, vSm->getState(MOVING_RIGHT));

    vSm->getState(BURNING_LEFT)->setTransition(DIE, vSm->getState(DEATH));
    vSm->getState(BURNING_RIGHT)->setTransition(DIE, vSm->getState(DEATH));


    vSm->getState(MOVING_LEFT)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->getState(MOVING_RIGHT)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->getState(DEATH)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->getState(SHOOTING_LEFT)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->getState(SHOOTING_RIGHT)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->getState(BURNING_LEFT)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->getState(BURNING_RIGHT)->addSubscription(State<cTank>::SUBSCRIPTION_IN,
                                                   &cTank::ChangeFramestate);
    vSm->setInitialState(OFF);

    sm = (StateMachine<cLateralAgent> *)vSm;
}

int cTank::ChangeFramestate(int trans, int oldSt, int newSt){
    bool solved=false;
    /*
    if(oldSt==SHOOTING_LEFT || oldSt==SHOOTING_RIGHT){
        cout << trans <<"\n";
    }
     */
    if(trans==GO_AHEAD){
        if(newSt==MOVING_RIGHT){
            SetSpeedX(GO);
        }else if(newSt==MOVING_LEFT){
            SetSpeedX(-GO);
        }else{
            SetSpeedX(GO);
        }
        SetFrameState(GetViewState(newSt));
    }else if(trans==REACHED){
            SetSpeedX(0);
            SetFrameState(GetViewState(newSt));
    }else if(trans==SHOOT){
            SetSpeedX(0);
            SetFrameState(GetViewState(newSt));
    }else if(trans==DIE){
        SetSpeedX(0);
        this->SetSurfaceType(NORTH_SURFACE, SOFT_SURFACE);
        this->SetSurfaceType(SOUTH_SURFACE, SOFT_SURFACE);
        this->SetSurfaceType(EAST_SURFACE, SOFT_SURFACE);
        this->SetSurfaceType(WEST_SURFACE, SOFT_SURFACE);
        if(oldSt==BURNING_LEFT){
            newSt++;
        }
        SetFrameState(GetViewState(newSt));
    }else{
        cAutonomousAgent::ChangeFramestate(trans, oldSt, newSt);
    }

    return State<cLateralAgent>::CONTINUE;
}


void cTank::Notify(cPhysicalReaction *reaction){
    if(!isDead()){
        if(reaction->GetReactionType()==cPhysicalReaction::COLLISION){
            if(reaction->GetReactionEvent()==cCollisionEvent::EAST_COLLISION){
                GoLeft();
            }else if(reaction->GetReactionEvent()==cCollisionEvent::WEST_COLLISION){
                GoRight();
            }
        }else if(reaction->GetReactionType()==cPhysicalReaction::VISION){
            localizeEnemy(((cVisionEvent*) reaction)->GetVisibleMap());
        }
        cAutonomousAgent::Notify(reaction);
    }
}

bool cTank::NotifyAgentCollision(cAgentCollisionEvent* event){
	bool ret = true;
        //matar o morir
        if((GetSurfaceType(NORTH_SURFACE)<event->GetSurfaceType()[SOUTH_SURFACE])
                    &&(event->GetArea()->hasIncluded(event->GetReactionEvent(), c2DArea::INCLUDE_SOUTH))
                || (GetSurfaceType(SOUTH_SURFACE)<event->GetSurfaceType()[NORTH_SURFACE])
                    &&(event->GetArea()->hasIncluded(event->GetReactionEvent(), c2DArea::INCLUDE_NORTH))
                || (GetSurfaceType(EAST_SURFACE)<event->GetSurfaceType()[WEST_SURFACE])
                    &&(event->GetArea()->hasIncluded(event->GetReactionEvent(), c2DArea::INCLUDE_WEST))
                || (GetSurfaceType(WEST_SURFACE)<event->GetSurfaceType()[EAST_SURFACE])
                    &&(event->GetArea()->hasIncluded(event->GetReactionEvent(), c2DArea::INCLUDE_EAST))
                    ){
            if(timeToFire<=ShotInterval-9){
                //morir
                sm->processTransition(REACHED);
                ret=false;
            }
        }else{
            if(event->GetArea()->hasIncluded(event->GetReactionEvent(), c2DArea::INCLUDE_EAST)){
                GoLeft();
                ret=false;
            }else if(event->GetArea()->hasIncluded(event->GetReactionEvent(), c2DArea::INCLUDE_WEST)){
                GoRight();
                ret=false;
            }
        }
	return ret;
}

void cTank::addEnemy(int enemy){
    enemyPattern.push_back(enemy);
}

void cTank::clearEnemies(){
    enemyPattern.clear();
}
void cTank::SetBulletTexture(cTexture* BulletTexture) {
    this->BulletTexture = BulletTexture;
}
cTexture* cTank::GetBulletTexture() const {
    return BulletTexture;
}

void cTank::removeEnemy(int enemyToRemove){
    list<int>::iterator it=enemyPattern.begin();
    while(it!=enemyPattern.end() && (*it)!=enemyToRemove){
        it++;
    }
    if(it!=enemyPattern.end()){
        enemyPattern.erase(it);
    }
}

void cTank::Shoot(cBullet* bullet){
    sm->processTransition(SHOOT);
    SetSpeedX(0);
    children.push(bullet);
    timeToFire=ShotInterval;
}

void cTank::NextStep(int time){
    cAutonomousAgent::NextStep(time);
    if(timeToFire>0){
        timeToFire-=time;
    }
    if(sm->getCurrentState()->getId()==BURNING_LEFT && GetFrameSeq()==6){
        sm->processTransition(DIE);
    }
    if(sm->getCurrentState()->getId()==BURNING_RIGHT && GetFrameSeq()==6){
        sm->processTransition(DIE);
    }
    /*
    if(sm->getCurrentState()->getId()==SHOOTING_LEFT
            || sm->getCurrentState()->getId()==SHOOTING_RIGHT){
        cout << GetFrameSeq() <<"\n";
    }
    */
    if(sm->getCurrentState()->getId()==SHOOTING_LEFT && GetFrameSeq()==15){
        sm->processTransition(GO_AHEAD);
    }
    if(sm->getCurrentState()->getId()==SHOOTING_RIGHT && GetFrameSeq()==15){
        sm->processTransition(GO_AHEAD);
    }
}


void cTank::localizeEnemy(vector< vector<int> >* VisibleMap){
    int x;
    int y;
    localizedEnemy=false;
    for(x=0; !localizedEnemy && x<VisibleMap->size(); x++ ){
        for(y=0; !localizedEnemy && y<(*(VisibleMap))[x].size();y++){
            for(list<int>::iterator it = enemyPattern.begin();
                        !localizedEnemy && it!=enemyPattern.end(); it++){
                if((*(VisibleMap))[x][y]==(*it)){
                    //enemigo localizado!
                    //deducir i establecer coordenadas de localización
                    //y tomar decisiones
                    localizedEnemy=true;
                   int dist=x-VisibleMap->size()/2;
                   int absDist = abs(dist);
                    if(timeToFire>0){
                       if(absDist!=0 && absDist<5){
                           //retroceder
                           awayToENenmy(8*dist/absDist);
                       }else if(absDist!=0 && absDist>8){
                           //aproximarse
                            approachToENenmy(6*dist/absDist);
                       }
                    }else{
                       if(absDist!=0 && absDist<5){
                           //retroceder
                           awayToENenmy(8*dist/absDist);
                       }else{
                           //disparar
                           cBullet* bullet;
                           bullet = PrepareShot(dist);
                           Shoot(bullet);
                       }
                    }
                }
            }
        }
    }
}

cBullet* cTank::PrepareShot(int dist){
    float force;
    int angle;
    bool r=true;
    int offsetx=70;
    int dangle=1;
    cBullet* bullet;
    if(dist>0){
        if(sm->getCurrentState()->getId()==MOVING_LEFT){
        //cambiar de dirección
            GoRight();
        }
    }else{
        if(sm->getCurrentState()->getId()==MOVING_RIGHT){
            //cambiar de dirección
            GoLeft();
        }
        r = false;
        dangle=-1;
        offsetx=10;
        dist=-dist;
    }
    //crear bala
    bullet = new cBullet();
    bullet->SetNom("Bullet");
    //apuntar
    force=1.2*dist;
    angle = dangle*40;
    bullet->Init(BulletTexture, PosX+offsetx, PosY+70, force, angle);
    return bullet;
}
void cTank::approachToENenmy(int dist){
    if(dist>0){
            GoRight();
    }else{
            GoLeft();
    }
    localizedEnemy = false;
}
void cTank::awayToENenmy(int dist){
    if(dist>0){
            GoLeft();
    }else{
            GoRight();
    }
    localizedEnemy = false;
}

bool cTank::isDead(){
    return sm->getCurrentState()->getId()==DEATH;
}

